package ch.uzh.ifi.sonar.qacpp;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/*
 * @Author: Yi Guo
 * Analyzing the metrics in a met file. The list of metrics is as follows:
 * 
 * STCDN Comment to code ratio
 * STOPT Halstead distinct operators
 * STTLN Total preprocessed code lines
 * STTOT Total number of tokens used
 * STTPP Total unpreprocessed source lines
 * STVAR Number of identifiers
 * STCCA Total Number of Characters
 * STCCB Total Number of Code Characters
 * STCCC Total Number of Comment Characters
 * 
 * 
 * */

/*
 * Halstead complexity measures are software metrics introduced by Maurice Howard Halstead in 1977
 as part of his treatise on establishing an empirical science of software development. 
 Halstead makes the observation that metrics of the software should reflect the implementation or expression of algorithms in different languages, 
 but be independent of their execution on a specific platform. These metrics are therefore computed statically from the code.
 Halstead's goal was to identify measurable properties of software, and the relations between them. 
 This is similar to the identification of measurable properties of matter (like the volume, mass, 
 and pressure of a gas) and the relationships between them (such as the gas equation). 
 Thus his metrics are actually not just complexity metrics.
 * */

public class MetFileAnalyzer {

	/* get output path from the settings.via */
	public static String getMetPath(String settingFilePath) {
		File settingFile = new File(settingFilePath);
		FileInputStream fis;
		try {
			fis = new FileInputStream(settingFile);
			BufferedReader reader = new BufferedReader(new InputStreamReader(
					fis));
			Pattern p = Pattern.compile("-op\\s+(.*)");
			Matcher matcher = p.matcher("");
			String line = null;
			while ((line = reader.readLine()) != null) {
				matcher.reset(line);
				while (matcher.find()) {
					String resultStr = matcher.replaceAll("$1");
					return resultStr.replaceAll("\"", "");
				}
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return "";
	}

	private final File metFile;

	public MetFileAnalyzer(File metFile) {
		this.metFile = metFile;
	}

	public MetFileAnalyzer(String metFileName) {
		this.metFile = new File(metFileName);
	}

	private long aggregateMetric(Pattern p) {
		long resultNum = 0;
		try {
			if (this.metFile.exists()) {
				FileInputStream fis = new FileInputStream(this.metFile);
				BufferedReader in = new BufferedReader(new InputStreamReader(
						fis));
				String aLine = null;
				Matcher m = p.matcher("");
				try {
					while ((aLine = in.readLine()) != null) {
						m.reset(aLine);
						while (m.find()) {
							String resultStr = m.replaceAll("$1");
							resultNum = resultNum + Long.parseLong(resultStr);
						}
					}
					in.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			} else {
				System.out.println(this.metFile.getAbsolutePath()
						+ " could not be found. ");
				return resultNum;
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		return resultNum;
	}

	/*
	 * public String getFileName() { String metName = this.metFile.getName();
	 * return metName.substring(0, metName.lastIndexOf(".met")); }
	 */

	public String getSourceFileName() {
		String metName = this.metFile.getAbsolutePath();
		int firstSlash = metName.indexOf("\\");
		int secondSlash = metName.indexOf("\\", firstSlash + 1);

		return metName.substring(secondSlash + 1, metName.lastIndexOf(".met"));
	}

	public File getMetFile() {
		return metFile;
	}

	private long getMetric(Pattern p) {
		try {
			if (this.metFile.exists()) {
				FileInputStream fis = new FileInputStream(this.metFile);
				BufferedReader in = new BufferedReader(new InputStreamReader(
						fis));
				String aLine = null;
				Matcher m = p.matcher("");
				try {
					while ((aLine = in.readLine()) != null) {
						m.reset(aLine);
						while (m.find()) {
							String resultStr = m.replaceAll("$1");
							return Long.parseLong(resultStr);
						}
					}
					in.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			} else {
				System.out.println(this.metFile.getAbsolutePath()
						+ " could not be found. ");
				return 0;
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();

		}
		System.out.println(this.metFile.getAbsolutePath()
				+ " could not calculate this metric, which pattern is: "
				+ p.toString());
		return 0;
	}

	/**
	 * 
	 * Class-based metrics calculation STCBO Coupling between objects STDIT
	 * Deepest level of inheritance STLCM Lack of cohesion of methods within a
	 * class STMTH Number of methods available in class STNOC Number of
	 * immediate children STNOP Number of immediate parents STRFC Response for
	 * class STWMC Weighted methods per class
	 * 
	 */
	public long getSTCBO() {
		Pattern p = Pattern.compile("<S>STCBO\\s(\\d+)");
		return getMetric(p);
	}

	/* STCCB Total Number of Code Characters */
	public long getSTCCB() {
		Pattern p = Pattern.compile("<S>STCCB\\s(\\d+)");
		return getMetric(p);
	}

	/**
	 * Cross Module Analysis, see the QACPP user guide STCYC Cyclomatic
	 * complexity STGTO Number of goto statements STLIN Number of maintainable
	 * lines of code STMIF Maximum nesting of control structures STPAR Number of
	 * parameters STPTH Estimated static path count STSUB Number of Function
	 * Calls STXLN Number of executable lines
	 * 
	 * @param p
	 * @return
	 */
	/** function based: Cyclomatic complexity */
	public long getSTCYC() {
		Pattern p = Pattern.compile("<S>STCYC\\s(\\d+)");
		return aggregateMetric(p);
	}

	/** class based: Deepest level of inheritance */
	public long getSTDIT() {
		Pattern p = Pattern.compile("<S>STDIT\\s(\\d+)");
		return aggregateMetric(p);
	}

	/** function based: Number of goto statements */
	public long getSTGTO() {
		Pattern p = Pattern.compile("<S>STGTO\\s(\\d+)");
		return aggregateMetric(p);
	}

	/** class based: Lack of cohesion of methods within a class */
	public long getSTLCM() {
		Pattern p = Pattern.compile("<S>STLCM\\s(\\d+)");
		return aggregateMetric(p);
	}

	/** function based: mainaintable Lines of Code */
	public long getSTLIN() {
		Pattern p = Pattern.compile("<S>STLIN\\s(\\d+)");
		return aggregateMetric(p);
	}

	/** function based: Maximum nesting of control structures */
	public long getSTMIF() {
		Pattern p = Pattern.compile("<S>STMIF\\s(\\d+)");
		return aggregateMetric(p);
	}

	/** class based: Number of methods available in class */
	public long getSTMTH() {
		Pattern p = Pattern.compile("<S>STMTH\\s(\\d+)");
		return aggregateMetric(p);
	}

	/** class based: Number of immediate children */
	public long getSTNOC() {
		Pattern p = Pattern.compile("<S>STNOC\\s(\\d+)");
		return aggregateMetric(p);
	}

	/** class based: STNOP Number of immediate parents */
	public long getSTNOP() {
		Pattern p = Pattern.compile("<S>STNOP\\s(\\d+)");
		return aggregateMetric(p);
	}

	/** file based: STOPT Halstead distinct operators */
	public long getSTOPT() {
		Pattern p = Pattern.compile("<S>STOPT\\s(\\d+)");
		return aggregateMetric(p);
	}

	/** function based: STPAR Number of parameters */
	public long getSTPAR() {
		Pattern p = Pattern.compile("<S>STPAR\\s(\\d+)");
		return aggregateMetric(p);
	}

	/** file based: STPTH Estimated static path count */
	public long getSTPTH() {
		Pattern p = Pattern.compile("<S>STPTH\\s(\\d+)");
		return aggregateMetric(p);
	}

	/** class based: STRFC Response for class */
	public long getSTRFC() {
		Pattern p = Pattern.compile("<S>STRFC\\s(\\d+)");
		return aggregateMetric(p);
	}

	/** function based: STSUB Number of Function Calls */
	public long getSTSUB() {
		Pattern p = Pattern.compile("<S>STSUB\\s(\\d+)");
		return aggregateMetric(p);
	}

	/** file based: STTLN Total preprocessed code lines */
	public long getSTTLN() {
		Pattern p = Pattern.compile("<S>STTLN\\s(\\d+)");
		return getMetric(p);
	}

	/** file based: STVAR Number of identifiers */
	public long getSTVAR() {
		Pattern p = Pattern.compile("<S>STVAR\\s(\\d+)");
		return aggregateMetric(p);
	}

	public long getSTWMC() {
		Pattern p = Pattern.compile("<S>STWMC\\s(\\d+)");
		return aggregateMetric(p);
	}

	public long getSTXLN() {
		Pattern p = Pattern.compile("<S>STXLN\\s(\\d+)");
		return aggregateMetric(p);
	}

	/**
	 * return the Nth substring's index, if the substring does not exist, return
	 * -1
	 */
	public static int getSubStrIndex(int number, String subString, String str) {
		int lastIndex = 0;
		int count = 0;
		while (lastIndex != -1) {
			lastIndex = str.indexOf(subString, lastIndex);
			if (count < number) {
				count++;
				lastIndex += subString.length();
			} else {
				return lastIndex;
			}
		}
		return -1;
	}

}
